/****************************************************
 * File: gtkmerge.c
 *
 * Author: Fred Engler
 *
 * Description: Code to merge two contigs via 
 * Edit->Merge ctgs
 ****************************************************/

# include "gtkmerge.h"

extern int track_num;
extern GtkWidget *tracks[];
extern float display_zoom;
extern GtkWidget *page_entry2;

extern int compare_clones_left();
extern int compare_clones_name();
extern void setmarkerposctg();
extern void update_page_numbers();
extern void compute_track_size();
extern void refresh_all_track_colors();
extern void move_selected();
extern void flipcontig();
extern void zero_left_end();

int merge_ctg_num;              /* The contig to merge with*/
GtkWidget *offset_entry;        /* GtkEntry for the gap between the contigs*/
int called_by_merge=0;          /* Used to suppress printing in move_selected*/
int flip1, flip2;               /* Toggles for flipping the contigs when merging*/

static void merge_display2();

/**************************************************************
                 DEF: sortctg
cari - 10.5.4 added so that works in fp and gel window during merge
***************************************************************/
static int sortroot(const void *orig_a, const void *orig_b)
{
  int x1, x2, ctg1, ctg2;
  struct contig *a;
  struct contig *b;

   a = (struct contig *)orig_a;
   b = (struct contig *)orig_b;
   x1 = arr(acedata, a->next, CLONE).x;
   x2 = arr(acedata, b->next, CLONE).x;
   ctg1 = arr(acedata, a->next, CLONE).ctg;
   ctg2 = arr(acedata, b->next, CLONE).ctg;
   if (ctg1==currentctg && ctg1!=ctg2) return -1;
   if (ctg2==currentctg && ctg1!=ctg2) return 1;
   if (x1 < x2) return -1;
   if (x1 > x2) return 1;
   return 0;
}
/*                     DEF: add_ctg_to_root
 * Adds the clones from ctg to the root structure*/
static void 
add_ctg_to_root(int ctg){
  int count;
  CLONE *clone;
  int i, index;
  int next;
  int maxright=0;

  if(root != NULL)
    messfree(root);

  count = contigs[currentctg].count + contigs[ctg].count;
  root = (struct contig *)messalloc(sizeof(struct contig)*count);

  /* Basically do the same as find_Clone on our resized root*/ 
  next = contigs[currentctg].start;

  for (i=0; i < contigs[currentctg].count && next != -1; i++)
  {
      clone =  arrp(acedata, next, CLONE);
      if(clone->ctg != currentctg){
        printf("FPC ERROR clone %s is in ctg %d and not %d\n",
               clone->clone,clone->ctg,currentctg);
        return;
      }
      root[i].next = next;
      root[i].new = (struct contig *)  &root[i+1];
      root[i].box = root[i].chbox = 0;
      next = clone->next;
  }
  if (i!=contigs[currentctg].count || next != -1) {
      printf("FPC ERROR ctg count %d next %d\n", contigs[currentctg].count,next);
      return;
  }

  /* Taken from find_Clone routine, with only minimal changes made
   * to append to the root*/
  next = contigs[ctg].start;

  for (i=0; i < contigs[ctg].count && next != -1; i++)
  {
      index = i + contigs[currentctg].count;  /* Added for merge*/
      clone =  arrp(acedata, next, CLONE);
      if(clone->ctg != ctg){
        printf("FPC ERROR clone %s is in ctg %d and not %d\n",
               clone->clone,clone->ctg,ctg);
        return;
      }
      if(clone->y > maxright) maxright=clone->y - page_end_left;
      root[i + contigs[currentctg].count].next = next;
      if (i< contigs[ctg].count - 1)
         root[index].new = (struct contig *)  &root[index + 1];
      else root[index].new = NULL;
      root[index].box = root[index].chbox = 0;
      next = clone->next;
  }
  if (i!=contigs[ctg].count || next != -1) {
      printf("FPC ERROR ctg count %d next %d\n", contigs[ctg].count,next);
      return;
  }
  /* cari 10.5.4 need sorted for fp and gel window */
  qsort(root, count, sizeof(struct contig), sortroot);
  for(i=0;i<count-1;i++) root[i].new = &(root[i+1]);
  root[i].new = NULL;
}

/*                     DEF: add_ctg_to_markerlistroot
 * Adds the markers from ctg to the markerlistroot structure*/
static void 
add_ctg_to_markerlistroot(int ctg){
  struct marker *tmp;
  struct markerctgpos *p2;
  struct markerlist *markerptr=NULL;
  BOOL first = FALSE,added;
  int l;

  setmarkerposctg();
 
  if (markerlistroot==NULL){
    Nmarkers=0;
    first=TRUE;
  }
  else{
    Nmarkers=1;
    for(markerptr=markerlistroot; markerptr->next!=NULL; markerptr=markerptr->next)
      Nmarkers++;
  }

  /* Hijacked from setmarkerposctg */
  for(l=0;l<arrayMax(markerdata);l++){
    tmp = arrp(markerdata,l,MARKER);
    tmp->box = 0;
    tmp->colour = 0;
    p2=tmp->pos;
    added = FALSE;
    while(p2!=NULL && !added){
      if(p2->ctg == ctg){
	if(first){
	  markerlistroot = (struct markerlist *)
	    messalloc((sizeof(struct markerlist)));
	  markerlistroot->markerindex = l;
	  markerlistroot->next = NULL;
	  markerptr = markerlistroot;
	  markerlistroot->midpt = p2->pos + mergeoffset;
	  first = FALSE;
	  added = TRUE;
	  Nmarkers = 1;
	}
	else{
	  markerptr->next =  (struct markerlist *)
	    messalloc((sizeof(struct markerlist)));
	  markerptr = markerptr->next;
	  markerptr->markerindex = l;
	  markerptr->next = NULL;
	  markerptr->midpt = p2->pos + mergeoffset;
	  Nmarkers++;
	  added = TRUE;
	}
      }
      p2 = p2->next;
    }
  }
}

/*                     DEF: merge_contigs
 * Peform an initial merge of the two contigs.*/
static void
merge_contigs (int ctgA, int ctgB){
  int i;
  GtkTrack *t;

  mergecontig1 = ctgA;
  mergecontig2 = ctgB;

  page_end_right = contigs[ctgA].right + contigs[ctgB].right + mergespace;
  update_page_numbers();

  add_ctg_to_root(ctgB);
  add_ctg_to_markerlistroot(ctgB);

  for(i=0; i<track_num; i++){
    t=GTK_TRACK(tracks[i]);
    gtk_track_clear_data(t);
    gtk_track_fill_data(t);
    compute_track_size(t);
  }
  centre_pos=contigs[ctgA].right + mergespace/2;
  refresh_all_track_colors();
}

/*                     DEF: do_merge_contigs
 * Applies the merge to the fpc structs.  This requires modifing affected
 * fields in the acedata structure.*/
static void
do_merge_contigs(){
  struct contig *cptr;
  CLONE *clp;
  int oldctg=0;
  int first=TRUE;
  char stmp[50];
  void AutoCtgMsg();

  called_by_merge=TRUE;
  for(cptr=root;cptr!=NULL;cptr=cptr->new){   /*"new" is next; "next" is clone index*/
    clp=arrp(acedata, cptr->next, CLONE);
    if(clp->ctg != currentctg){
      clp->selected=TRUE;
      if(first){
	oldctg=clp->ctg;
	first=FALSE;
      }
    }
  }
  if(oldctg!=0) {
    int save = -1;   /* cari 20apr5 */
    if (contigs[oldctg].ctgQs != -1 && contigs[currentctg].ctgQs != -1)
         save = contigs[oldctg].ctgQs + contigs[currentctg].ctgQs;
    move_selected(oldctg, currentctg, mergeoffset); 
    if (save != -1) {
       contigs[currentctg].ctgQs = save;
       contigs[currentctg].approxQs = 1;
    }
    sprintf(stmp,"User-merge ctg%d.",oldctg); 
    AutoCtgMsg(currentctg, stmp);
  }
  called_by_merge=FALSE;
}

/*                     DEF: ctg_entry_callback
 * Callback for the contig-to-merge-with text entry*/
static void
ctg_entry_callback(GtkWidget *widget, gpointer data){
  sscanf(gtk_entry_get_text(GTK_ENTRY(widget)), "%d", &merge_ctg_num);
}

/*                     DEF: flip_button1_callback
 * Flips the current contig*/
static void
flip_button1_callback(GtkWidget *widget, gpointer data){
  flip1 = !flip1;
  flipcontig(currentctg);
}

/*                     DEF: flip_button2_callback
 * Flags the second contig to be flipped*/
static void
flip_button2_callback(GtkWidget *widget, gpointer data){
  if (GTK_TOGGLE_BUTTON (widget)->active) flip2=TRUE;
  else flip2=FALSE;
}

/*                     DEF: cancel_button_callback
 * Cancel for the first merge window*/
static void
cancel_button_callback(GtkWidget *widget, gpointer data){
  gtk_widget_destroy(merge_window1);
  merge_window1=NULL;
}

/*                     DEF: start_button_callback
 * Start button callback*/
static void
start_button_callback(GtkWidget *widget, gpointer data){
  if(merge_ctg_num==0){
    displaymess("Cannot merge with Ctg 0");
    return;
  }
  else if(currentctg==merge_ctg_num){
    displaymess("Cannot merge the same contig");
    return;
  }
  else if((merge_ctg_num > max_contig) || (contigs[merge_ctg_num].count==0)){
    printf("Contig %d does not exist\n", merge_ctg_num);
    return;
  }
  merging=TRUE;
  zero_left_end(merge_ctg_num);
  if(flip2) flipcontig(merge_ctg_num);
  gtk_widget_destroy(merge_window1);
  merge_window1=NULL;
  merge_contigs(currentctg, merge_ctg_num);
  merge_display2();
}

/*                     DEF: merge_display1
 * Graphics stuff for the first merge display.*/
void merge_display1(){
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *label;
  GtkWidget *flip_button1;
  GtkWidget *flip_button2;
  GtkWidget *ctg_entry;
  GtkWidget *cancel_button;
  GtkWidget *start_button;

  char str[7];

  merge_ctg_num = 0;
  flip1 = flip2 = FALSE;
  mergespace=20;
  mergeoffset=contigs[currentctg].right + mergespace;

  merge_window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_container_set_border_width (GTK_CONTAINER (merge_window1), 5);
  gtk_window_set_title (GTK_WINDOW (merge_window1), "Merge contigs");
  gtk_window_set_policy (GTK_WINDOW (merge_window1), FALSE, TRUE, FALSE);
  gtk_signal_connect (GTK_OBJECT (merge_window1), "destroy",
		      GTK_SIGNAL_FUNC (cancel_button_callback), NULL);


  vbox=gtk_vbox_new(FALSE, 10);
  gtk_container_add (GTK_CONTAINER (merge_window1), vbox);

  /* Contig A*/
  hbox=gtk_hbox_new(FALSE, 10);
  label = gtk_label_new("Ctg");
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

  sprintf(str, "%d",currentctg);
  label = gtk_label_new(str);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, FALSE, 0);

  flip_button1 = gtk_button_new_with_label("Flip");
  gtk_signal_connect (GTK_OBJECT (flip_button1), "clicked",
                      GTK_SIGNAL_FUNC (flip_button1_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), flip_button1, FALSE, FALSE, 0);

  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  /* Contig B*/
  hbox=gtk_hbox_new(FALSE, 10);
  label = gtk_label_new("Ctg");
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

  ctg_entry=gtk_entry_new_with_max_length(5);
  gtk_signal_connect (GTK_OBJECT (ctg_entry), "changed",
		      GTK_SIGNAL_FUNC (ctg_entry_callback), NULL);
  gtk_widget_set_usize(ctg_entry, 45, -2);
  gtk_box_pack_start(GTK_BOX(hbox), ctg_entry, TRUE, FALSE, 0);

  flip_button2 = gtk_toggle_button_new_with_label("Flip");
  gtk_signal_connect (GTK_OBJECT (flip_button2), "clicked",
                      GTK_SIGNAL_FUNC (flip_button2_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), flip_button2, FALSE, FALSE, 0);

  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  hbox=gtk_hbox_new(FALSE, 5);

  cancel_button = gtk_button_new_with_label("Cancel");
  gtk_signal_connect (GTK_OBJECT (cancel_button), "clicked",
                      GTK_SIGNAL_FUNC (cancel_button_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), cancel_button, TRUE, TRUE, 0);

  start_button = gtk_button_new_with_label("Start merge");
  gtk_signal_connect (GTK_OBJECT (start_button), "clicked",
                      GTK_SIGNAL_FUNC (start_button_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), start_button, TRUE, TRUE, 0);

  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  gtk_widget_show_all(merge_window1);
}


/*                     DEF: left_button1_callback
 * Move contigs closer together in a big step*/ 
static void
left_button1_callback(GtkWidget *widget, gpointer data){
  char offset_text[8];

  mergeoffset-=10;
  if(mergeoffset<0){ 
    displaymess("Cannot move past 0");
    mergeoffset=0;
    mergespace= -(page_end_right-page_end_left+1);
  }
  else
    mergespace-=10;
  sprintf(offset_text,"%d",mergespace);
  gtk_entry_set_text(GTK_ENTRY(offset_entry),offset_text); 
  merge_contigs(currentctg, merge_ctg_num);
}

/*                     DEF: left_button2_callback
 * Move contigs closer together in a small step*/ 
static void
left_button2_callback(GtkWidget *widget, gpointer data){
  char offset_text[8];

  mergeoffset-=1;
  if(mergeoffset<0){ 
    displaymess("Cannot move past 0");
    mergeoffset=0;
    mergespace= -(page_end_right-page_end_left+1);
  }
  else
    mergespace-=1;
  sprintf(offset_text,"%d",mergespace);
  gtk_entry_set_text(GTK_ENTRY(offset_entry),offset_text); 
  merge_contigs(currentctg, merge_ctg_num);
}

/*                     DEF: right_button1_callback
 * Move contigs further apart in a big step*/ 
static void
right_button1_callback(GtkWidget *widget, gpointer data){
  char offset_text[8];

  mergeoffset+=10;
  mergespace+=10;
  sprintf(offset_text,"%d",mergespace);
  gtk_entry_set_text(GTK_ENTRY(offset_entry),offset_text); 
  merge_contigs(currentctg, merge_ctg_num);
}

/*                     DEF: right_button2_callback
 * Move contigs further apart in a small step*/ 
static void
right_button2_callback(GtkWidget *widget, gpointer data){
  char offset_text[8];

  mergeoffset+=1;
  mergespace+=1;
  sprintf(offset_text,"%d",mergespace);
  gtk_entry_set_text(GTK_ENTRY(offset_entry),offset_text); 
  merge_contigs(currentctg, merge_ctg_num);
}

// anne apr06 commented out as does not work properly
/*                     DEF: offset_entry_callback
 * Direct entry of space between contigs
static void
offset_entry_callback(GtkWidget *widget, gpointer data){
  int offset;
  int temp;
  char offset_text[8];

  sscanf(gtk_entry_get_text(GTK_ENTRY(offset_entry)), "%d", &offset);
  temp=(page_end_right-page_end_left+1) + offset;
  if(temp<0){ 
    displaymess("Cannot move past 0");
    sprintf(offset_text,"%d",mergespace);
    gtk_entry_set_text(GTK_ENTRY(offset_entry),offset_text); 
  }
  else{
    mergeoffset=temp;
    mergespace = offset;
  }
  merge_contigs(currentctg, merge_ctg_num);
}
*/

/*                     DEF: destroy_merge_window2_callback
 * Close the second merge window.  This puts the contigs back
 * in their original condition and and redisplays the current contig,
 * which fills the root struct with only the clones for the current contig,
 * and doesn't include the merged clones because the acedata was not changed.*/
static void
destroy_merge_window2_callback(GtkWidget *widget, gpointer data){
  if(merge_window2==NULL) return;
  if(flip1) flipcontig(currentctg);
  if(flip2) flipcontig(merge_ctg_num);
  merge_window2=NULL;
  merging=FALSE;
  ctgdisplay(currentctg);
}

/*                     DEF: reject_button_callback
 * Don't do the merge.*/
static void
reject_button_callback(GtkWidget *widget, gpointer data){
   /* cari 4 feb 05 */
  printf("Reject merge of ctg%d and ctg%d\n",mergecontig1, mergecontig2);
  page_end_right = contigs[mergecontig1].right;
  update_page_numbers();
  gtk_widget_destroy(merge_window2);
}

/*                     DEF: accept_button_callback
 * Apply the merge. do_merge_contigs will modify the acedata.*/
static void
accept_button_callback(GtkWidget *widget, gpointer data){
  gtk_signal_handler_block_by_func(GTK_OBJECT (merge_window2), 
                                   GTK_SIGNAL_FUNC(destroy_merge_window2_callback), 
                                   NULL);
  gtk_widget_destroy(merge_window2);
  merge_window2=NULL;
  do_merge_contigs();
  merging=FALSE;
  ctgdisplay(currentctg);
  printf("Accept merge of ctg%d and ctg%d\n",mergecontig1, mergecontig2);
}

/*                     DEF: merge_display2
 * Graphics stuff for the second merge display.*/
static void 
merge_display2(){
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *frame1;
  GtkWidget *reject_button;
  GtkWidget *accept_button;
  GtkWidget *left_button1;
  GtkWidget *left_button2;
  GtkWidget *right_button1;
  GtkWidget *right_button2;
  char offset_text[8];

  merge_window2 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_container_set_border_width (GTK_CONTAINER (merge_window2), 5);
  gtk_window_set_title (GTK_WINDOW (merge_window2), "Confirm Merge contigs");
  gtk_window_set_policy (GTK_WINDOW (merge_window2), FALSE, TRUE, FALSE);
  gtk_signal_connect (GTK_OBJECT (merge_window2), "destroy",
		      GTK_SIGNAL_FUNC (destroy_merge_window2_callback), NULL);

  vbox=gtk_vbox_new(FALSE, 5);
  gtk_container_add (GTK_CONTAINER (merge_window2), vbox);

  frame1 = gtk_frame_new("Gap");
  gtk_box_pack_start(GTK_BOX(vbox), frame1, FALSE, FALSE, 0);

  hbox=gtk_hbox_new(FALSE, 3);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);

  left_button1 = gtk_button_new_with_label("<<");
  gtk_signal_connect (GTK_OBJECT (left_button1), "clicked",
                      GTK_SIGNAL_FUNC (left_button1_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), left_button1, TRUE, FALSE, 0);

  left_button2 = gtk_button_new_with_label("<");
  gtk_signal_connect (GTK_OBJECT (left_button2), "clicked",
                      GTK_SIGNAL_FUNC (left_button2_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), left_button2, TRUE, FALSE, 0);

  offset_entry=gtk_entry_new_with_max_length(4);
  // anne apr06 disallow the user from entering a value in the merge window. 
  gtk_entry_set_editable(GTK_ENTRY(offset_entry), FALSE); 
  //gtk_signal_connect (GTK_OBJECT (offset_entry), "activate",
  //	      GTK_SIGNAL_FUNC (offset_entry_callback), NULL);
  gtk_widget_set_usize(offset_entry, 45, -2);
  sprintf(offset_text,"%d",mergespace);
  gtk_entry_set_text(GTK_ENTRY(offset_entry),offset_text); 
  gtk_box_pack_start(GTK_BOX(hbox), offset_entry, TRUE, FALSE, 0);

  right_button2 = gtk_button_new_with_label(">");
  gtk_signal_connect (GTK_OBJECT (right_button2), "clicked",
                      GTK_SIGNAL_FUNC (right_button2_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), right_button2, TRUE, FALSE, 0);

  right_button1 = gtk_button_new_with_label(">>");
  gtk_signal_connect (GTK_OBJECT (right_button1), "clicked",
                      GTK_SIGNAL_FUNC (right_button1_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), right_button1, TRUE, FALSE, 0);

  gtk_container_add(GTK_CONTAINER(frame1), hbox);

  hbox=gtk_hbox_new(TRUE, 5);
  reject_button = gtk_button_new_with_label("Reject merge");
  gtk_signal_connect (GTK_OBJECT (reject_button), "clicked",
                      GTK_SIGNAL_FUNC (reject_button_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), reject_button, FALSE, FALSE, 0);

  accept_button = gtk_button_new_with_label("Accept merge");
  gtk_signal_connect (GTK_OBJECT (accept_button), "clicked",
                      GTK_SIGNAL_FUNC (accept_button_callback),
                      NULL);
  gtk_box_pack_start(GTK_BOX(hbox), accept_button, FALSE, FALSE, 0);

  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  gtk_widget_show_all(merge_window2);
}
